package server;

import server.container.StandardContext;
import server.container.StandardHost;
import server.container.StandardServletWrapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Mapper {


    private Map<String, MapHost> hostMap = new ConcurrentHashMap<>();


    public StandardServletWrapper getWrapper(String host, String context, String urlPattern) {
        try {
            MapHost mapHost = hostMap.get(host);
            MappedContext mappedContext = mapHost.getMappedContext(context);
            MappedWrapper wrapper = mappedContext.getWrapper(urlPattern);
            return wrapper.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 设置组件映射
     *
     * @param host
     * @param context
     * @param urlPattern
     * @param httpServlet
     */
    public void addWrapper(String host, String context, String urlPattern, HttpServlet httpServlet) {
        MapHost mapHost = hostMap.get(host);
        if (mapHost == null) {
            /**
             * 构建contextVersion
             */
            ContextVersion contextVersion = buildContextVersion(urlPattern, httpServlet);

            /**
             * 构建Context
             */
            ContextList contextList = buildContextList(context, contextVersion);

            /**
             * 构建Host
             */
            mapHost = new MapHost(host, new StandardHost(host));
            mapHost.setContextList(contextList);
            hostMap.put(host, mapHost);
        }

        MappedContext mappedContext = mapHost.getMappedContext(context);
        if (mappedContext == null) {
            ContextVersion contextVersion = new ContextVersion(new ArrayList<>());
            mappedContext = new MappedContext(context, new StandardContext(context));
            mappedContext.setContextVersion(contextVersion);
            mapHost.addContext(mappedContext);
        }

        MappedWrapper mappedWrapper = mappedContext.getWrapper(urlPattern);
        if (mappedWrapper == null) {
            mappedWrapper = new MappedWrapper(urlPattern, new StandardServletWrapper(httpServlet));
            mappedContext.addWrapper(mappedWrapper);
        }
    }

    private ContextList buildContextList(String context, ContextVersion contextVersion) {
        StandardContext standardContextContainer = new StandardContext(context);
        MappedContext mappedContext = new MappedContext(context, standardContextContainer);
        mappedContext.setContextVersion(contextVersion);
        List<MappedContext> mappedContexts = new ArrayList<>();
        mappedContexts.add(mappedContext);
        return new ContextList(mappedContexts);
    }

    private ContextVersion buildContextVersion(String urlPattern, HttpServlet httpServlet) {
        List<MappedWrapper> wrappers = new ArrayList<>();
        wrappers.add(new MappedWrapper(urlPattern, new StandardServletWrapper(httpServlet)));
        return new ContextVersion(wrappers);
    }

    public StandardHost getHost(String host) {
        return hostMap.get(host).getObject();
    }



    protected abstract static class MapElement<T> {

        public final String name;
        public final T object;

        public MapElement(String name, T object) {
            this.name = name;
            this.object = object;
        }

        public String getName() {
            return name;
        }

        public T getObject() {
            return object;
        }
    }


    protected static class MapHost extends MapElement<StandardHost> {
        private ContextList contextList;

        public MapHost(String name, StandardHost object) {
            super(name, object);
        }

        public void setContextList(ContextList contextList) {
            this.contextList = contextList;
        }

        public ContextList getContextList() {
            return contextList;
        }

        public MappedContext getMappedContext(String context) {
            return contextList.getContext(context);
        }


        public void addContexts(ContextList contextList) {
            contextList.addContexts(contextList);
        }

        public void addContext(MappedContext mappedContext) {
            contextList.getContexts().add(mappedContext);
        }
    }

    protected static class MappedContext extends MapElement<StandardContext> {
        private ContextVersion contextVersion;

        public MappedContext(String name, StandardContext object) {
            super(name, object);
        }

        public ContextVersion getContextVersion() {
            return contextVersion;
        }

        public void setContextVersion(ContextVersion contextVersion) {
            this.contextVersion = contextVersion;
        }

        public MappedWrapper getWrapper(String urlPattern) {
            return contextVersion.getWrapper(urlPattern);
        }

        public void addWrapper(MappedWrapper mappedWrapper) {
            contextVersion.getWrappers().add(mappedWrapper);
        }
    }

    protected static class MappedWrapper extends MapElement<StandardServletWrapper> {
        public MappedWrapper(String name, StandardServletWrapper object) {
            super(name, object);
        }
    }

    protected static final class ContextList {
        public final List<MappedContext> contexts;

        public ContextList(List<MappedContext> contexts) {
            this.contexts = contexts;
        }

        public MappedContext getContext(String context) {
            for (MappedContext mappedContext : contexts) {
                if (mappedContext.getName().equals(context)) {
                    return mappedContext;
                }
            }
            return null;
        }

        public List<MappedContext> getContexts() {
            return contexts;
        }

        public void addContexts(ContextList contextList) {
            contexts.addAll(contextList.getContexts());
        }
    }

    protected static final class ContextVersion {
        public final List<MappedWrapper> wrappers;

        public ContextVersion(List<MappedWrapper> wrappers) {
            this.wrappers = wrappers;
        }

        public MappedWrapper getWrapper(String urlPattern) {
            for (MappedWrapper mappedWrapper : wrappers) {
                if (mappedWrapper.getName().equals(urlPattern)) {
                    return mappedWrapper;
                }
            }
            return null;
        }

        public List<MappedWrapper> getWrappers() {
            return wrappers;
        }
    }
}
